--[[
协议模板规则如下
1.在module文件夹下面建立的所有文件夹都是一个一个的模块，里面至少要有一个proto.lua和handler.lua文件
2.handlerL.lua里面填写该模块的cg_xxx，wl_xxx, ll_xxx函数
handlerW.lua里处理CG_XXX,LW_XXX协议
3.proto.lua里面填写该模块的cg和gc, wl,lw协议 ll_xxx协议为直接logic2logic协议
4.对于proto.lua里面的不是以cg和gc, wl, lw, ll开头的table，是作为子结构，其中，如果没有local开头的，那么子结构的名字将作为客户端对应的类的名字，如果有local开头或者是直接在table里面展开的子结构，客户端对应的类的名字将根据一层一层的字段自动生成
5.协议的每个字段{名字，容量，类型}
名字：只能用string，不能用number，并且不能以下划线开头（避免生成的跟客户端的私有变量冲突）
容量：为预分配的最大空间，当容量大于1的时候，该字段就必需包含长度，例如字段名为aa，aa就是一个table，那么长度就是aa[0]，aa[0]必需小于等于容量
类型：
{"aa", 1, "int"}	--32位整型
{"aa", 1, "double"}	--64位浮点数
{"aa", 1, "string", 128} --一个字符串，里面最多有128个字节，字符串长度上限不填的话，默认是64
{"aa", 1, sub}		--aa是一个table，里面的字段由子结构的模板sub决定

{"aa", 2, "int"}	--数组，里面最多有2个整型
{"aa", 2, "double"}	--数组，里面最多有2个64位浮点数
{"aa", 2, "string", 128}	--字符串数组，每个字符串里面最多有128个字节，字符串长度上限不填的话，默认是64
{"aa", 2, sub}		--aa是一个table，aa[1]也是一个table，aa[1]里面的字段由子结构的模板sub决定
--]]

local msg_ex = _G.msg_ex

cg = cg or {}
gc = gc or {}
wl = wl or {}   --world -> logic 协议在logic处理
lw = lw or {}   --logic -> world
ll = ll or {}   --logic -> logic

proto_handler = proto_handler or {}
proto_id = proto_id or {}
proto_name = proto_name or {}
proto_template = proto_template or {}
list = list or {} -- 消息发送对象列表 第0为长度 后面1-x为obj

local Config = require("Config")

local mt = {
    __index =
    function(t, k)
        local id = t[1] or ""
        assert(nil, id .. " " .. k .. " not exist")
    end
}

local function proto_container_metatable(msg)
    if not Config.IS_WINDOWS then
        return
    end
    setmetatable(msg, mt)
    for _, v in pairs(msg) do
        if type(v) == "table" then
            proto_container_metatable(v)
        end
    end
end

local function proto_container_resize(msg, template)
    for _, v in ipairs(template) do
        if v[2] < 2 then
            if type(v[3]) == "table" then
                cnt = cnt + 1
                msg[v[1]] = {}
                proto_container_resize(msg[v[1]], v[3])
            end
        else
            if type(v[3]) == "table" then
                local v2 = v[2]
                if v2 == 2 and (v[1] == "items" or v[1] == "equips" or v[1] == "gems") then
                    v2 = 1
                end
                cnt = cnt + 1
                msg[v[1]] = {}
                for i = 1, v2 do
                    msg[v[1]][i] = {}
                    proto_container_resize(msg[v[1]][i], v[3])
                end
            else
                cnt = cnt + 1
                msg[v[1]] = {}
            end
        end
    end
end

function register(name, id, template, handler)
    if not proto_name[id] then
        if _G.is_world then
            msg_ex.register_template(id, template)
        end
        if string.sub(name, 1, 2) == "CG" then
            cg[id] = {[1] = id}
            proto_container_resize(cg[id], template)
            proto_container_metatable(cg[id])
        elseif string.sub(name, 1, 2) == "GC" then
            gc[name] = {[1] = id}
            cnt = 0
            proto_container_resize(gc[name], template)
            if 8000 < cnt then
                --print(name, cnt)
            end
        elseif string.sub(name, 1,2) == "WL" then
            if _G['is_world'] then
                wl[name] = {[1] = id}
                proto_container_resize(wl[name], template)
            else
                wl[id] =  {[1] = id}
                proto_container_resize(wl[id], template)
                proto_container_metatable(wl[id])
            end
        elseif string.sub(name, 1, 2) == "LW" then
            if _G['is_world'] then
                lw[id] = {[1] = id}
                proto_container_resize(lw[id], template)
                proto_container_metatable(lw[id])
            else
                lw[name] =  {[1] = id}
                proto_container_resize(lw[name], template)
            end
        elseif string.sub(name, 1, 2) == "LL" then
            if not _G['is_world'] then
                ll[id] = {[1] = id}
                ll[name] = {[1] = id}
                proto_container_resize(ll[name], template)
                proto_container_resize(ll[id], template)
                proto_container_metatable(ll[id])
            end
        end
    end
    proto_handler[id] = handler
    proto_name[id] = name
    proto_template[id] = template
end

send = msg_ex.unicast

sendMulti = msg_ex.multicast

sendAll = msg_ex.broadcast


-- 线程内部通信用uuid 做key
if _G['is_world'] then
    uuid2logic  = uuid2logic or {}
    function set_logic(fd, uuid, logicid)
        uuid2logic[uuid] = logicid
        _G.set_fd_logic(fd, logicid or -1)
    end
    function send2logic(msg, uuid)
       msg_ex.send2logic(msg, uuid, uuid2logic[uuid])
    end
    function send2logic_id(msg,  id)   -- uuid为空，则直接发给线程
       msg_ex.send2logic(msg, "", id)
    end
else
    function send2world(msg, uuid)
        msg_ex.send2world(msg,uuid, -1, -1)
    end
    local logic_id = _G.logic_id
    function logic2logic(msg, uuid, dst)      --直接发到其它logic线程
        assert(logic_id ~= dst)
        msg_ex.send2world(msg, uuid, dst,logic_id)
    end
end

local out = {}
local out_len = 0
local function add_space(step)
    for i = 1, step do
        out_len = out_len + 1
        out[out_len] = "	"
    end
end

local set = {}
function trace(m, template, step, print_on_err)
    if not step then
        template = proto_template[m[1]]
        step = 0
        out_len = 0
    end
    for k in pairs(set) do
        set[k] = nil
    end
    for _, v in ipairs(template) do
        set[v[1]] = true
    end
    for k in pairs(m) do
        if k ~= 1 and not set[k] then
            print(table.concat(out, nil, 1, out_len))
            assert(nil, k .. " not need")
        end
    end
    for _, v in ipairs(template) do
        add_space(step)
        out_len = out_len + 1
        out[out_len] = v[1]
        out_len = out_len + 1
        out[out_len] = "="
        if v[2] < 2 then
            if type(v[3]) == "table" then
                if type(m[v[1]]) ~= "table" then
                    print(table.concat(out, nil, 1, out_len))
                    assert(nil, v[1] .. " is not table")
                end
                out_len = out_len + 1
                out[out_len] = "\n"
                trace(m[v[1]], v[3], step + 1, print_on_err)
            else
                if (v[3] == "bit" or v[3] == "double" or v[3] == "int") and type(m[v[1]]) ~= "number" then
                    print(table.concat(out, nil, 1, out_len))
                    assert(nil, v[1] .. " is not number")
                end
                if v[3] == "string" and type(m[v[1]]) ~= "string" then
                    print(table.concat(out, nil, 1, out_len))
                    assert(nil, v[1] .. " is not string")
                end
                out_len = out_len + 1
                out[out_len] = m[v[1]]
                out_len = out_len + 1
                out[out_len] = "\n"
            end
        else
            if type(m[v[1]]) ~= "table" then
                print(table.concat(out, nil, 1, out_len))
                assert(nil, v[1] .. " is not table")
            end
            out_len = out_len + 1
            out[out_len] = "\n"
            if type(m[v[1]][0]) ~= "number" then
                print(table.concat(out, nil, 1, out_len))
                assert(nil, v[1] .. " len is not number")
            end
            add_space(step + 1)
            out_len = out_len + 1
            out[out_len] = "0="
            out_len = out_len + 1
            out[out_len] = m[v[1]][0]
            out_len = out_len + 1
            out[out_len] = "\n"
            for i = 1, m[v[1]][0] do
                if type(v[3]) == "table" then
                    if type(m[v[1]][i]) ~= "table" then
                        print(table.concat(out, nil, 1, out_len))
                        assert(nil, v[1] .. " " .. i .. " is not table")
                    end
                    add_space(step + 1)
                    out_len = out_len + 1
                    out[out_len] = i
                    out_len = out_len + 1
                    out[out_len] = "="
                    out_len = out_len + 1
                    out[out_len] = "\n"
                    trace(m[v[1]][i], v[3], step + 2, print_on_err)
                else
                    if (v[3] == "bit" or v[3] == "double" or v[3] == "int") and type(m[v[1]][i]) ~= "number" then
                        print(table.concat(out, nil, 1, out_len))
                        assert(nil, v[1] .. " " .. i .. " is not number")
                    end
                    if v[3] == "string" and type(m[v[1]][i]) ~= "string" then
                        print(table.concat(out, nil, 1, out_len))
                        assert(nil, v[1] .. " " .. i .. " is not string")
                    end
                    add_space(step + 1)
                    out_len = out_len + 1
                    out[out_len] = i
                    out_len = out_len + 1
                    out[out_len] = "="
                    out_len = out_len + 1
                    out[out_len] = m[v[1]][i]
                    out_len = out_len + 1
                    out[out_len] = "\n"
                end
            end
        end
    end
    if step == 0 and not print_on_err then
        print(table.concat(out, nil, 1, out_len))
    end
end